#region (c) 2014 THN Solutions LLC. - All rights reserved
/*
Simplified BSD License (BSD)
Copyright (c) 2014, THN Solutions LLC. ( www.ThnSolutions.com )
Author: Nguyễn, M. Hải                 ( www.Minh-Hai.com     )
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this 
      list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright notice, 
      this list of conditions and the following disclaimer in the documentation 
      and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/
#endregion

#region Record of Change
//             							CHANGE HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | DEVELOPER  | DESCRIPTION                                              |
// -------------------------------------------------------------------------------------
// | 10-Nov-09 | HuyHM      | First creation.                                          |
// -------------------------------------------------------------------------------------
//
#endregion

#region Record of Audit
//             							REVIEW HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | REVIEWER   | NOTES                                                    |
// -------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------
//
#endregion

#region Using directives
using System;
using System.Collections;

using Thn;
#endregion

namespace Thn.Interface.Vcl
{
    /// <summary>
    /// Strongly typed collection of <see cref="ListboxColumn"/>.
    /// </summary>
    public class ListBoxColumnList : IList, ICollection, IEnumerable
    {
        #region Fields
        /// <summary>
        /// internal data
        /// </summary>
        internal ListBoxColumn[] mItems = null;
        /// <summary>
        /// Number of items currently in list
        /// </summary>
        internal int mCount = 0;

        [NonSerialized]
        private object mSynchRoot;
        #endregion

        #region Capacity
        private int mCapacity;
        /// <summary>
        /// Gets current maximal capacity
        /// </summary>
        public int Capacity
        {
            get { return mCapacity; }
        }
        #endregion

        #region IList Members
        public void Add(string title)
        {
            this.Add(new ListBoxColumn(title));
        }

        public void Add(string title, int width)
        {
            ListBoxColumn column = new ListBoxColumn(title);
            column.Width = width;
            this.Add(column);
        }
        #region Add
        /// <summary>
        /// Adds an item to the list
        /// </summary>
        public int Add(ListBoxColumn value)
        {
            //check if not enough space then allocate more
            if (mCount >= mCapacity)
            {
                //calculate new capacity
                int newCapacity;
                if (mCapacity < 1000) newCapacity = (int)(mCapacity * 2);
                else if (mCapacity < 5000) newCapacity = (int)(mCapacity * 1.5);
                else newCapacity = (int)(mCapacity * 1.2);

                //allocate new buffer
                ListBoxColumn[] newItems = new ListBoxColumn[newCapacity];

                //copy old values to new buffer
                mItems.CopyTo(newItems, 0);

                //assign the new bufer to current buffer
                mItems = newItems;
                mCapacity = newCapacity;
            }

            //assign the new value to the end of current buffer
            mItems[mCount] = value;
            mCount++;

            return mCount - 1;
        }

        /// <summary>
        /// Adds an item to the list
        /// </summary>
        int IList.Add(object value)
        {
            return Add((ListBoxColumn)value);
        }
        #endregion

        #region Clear
        /// <summary>
        /// Removes all items
        /// </summary>
        public void Clear()
        {
            Array.Clear(mItems, 0, mCount);
            mCount = 0;
        }
        #endregion

        #region Contains
        /// <summary>
        /// Determines whether the list contains a specific value
        /// </summary>
        public bool Contains(object value)
        {
            if (value == null)
            {
                //search for the first occurence of null item
                for (int i = 0; i < mCount; i++)
                {
                    if (mItems[i] == null) return true;
                }
            }
            else
            {
                //search for the first match
                for (int i = 0; i < mCount; i++)
                {
                    if (value.Equals(mItems[i])) return true;
                }
            }

            return false;
        }
        #endregion

        #region Index Of
        /// <summary>
        /// Determines the index of a specific item
        /// </summary>
        public int IndexOf(ListBoxColumn value)
        {
            if (value == null)
            {
                //search for the first occurence of null item
                for (int i = 0; i < mCount; i++)
                {
                    if (mItems[i] == null) return i;
                }
            }
            else
            {
                //search for the first match
                for (int i = 0; i < mCount; i++)
                {
                    if (value.Equals(mItems[i])) return i;
                }
            }

            return -1;
        }

        /// <summary>
        /// Determines the index of a specific item
        /// </summary>
        int IList.IndexOf(object value)
        {
            return IndexOf((ListBoxColumn)value);
        }
        #endregion

        #region Insert
        /// <summary>
        /// Inserts an item to the list at the specified index
        /// </summary>
        public void Insert(int index, ListBoxColumn value)
        {
            //check if not enough space then allocate more
            if (mCount >= mCapacity)
            {
                //calculate new capacity
                int newCapacity;
                if (mCapacity < 1000) newCapacity = (int)(mCapacity * 2);
                else if (mCapacity < 5000) newCapacity = (int)(mCapacity * 1.5);
                else newCapacity = (int)(mCapacity * 1.2);

                //allocate new buffer
                ListBoxColumn[] newItems = new ListBoxColumn[newCapacity];

                //copy old values from left of index to new buffer
                if (index > 0)
                {
                    Array.Copy(mItems, 0, newItems, 0, index);
                }

                //assign the new value to the index position of the new buffer
                newItems[index] = value;

                //copy old values from right of index to new buffer
                if (index < mCount)
                {
                    Array.Copy(mItems, index, newItems, index + 1, mCount - index);
                }

                //assign the new bufer to current buffer
                mItems = newItems;
                mCapacity = newCapacity;
                mCount++;
            }
            else
            {
                //copy old values from right of index to new buffer
                if (index < mCount)
                {
                    Array.Copy(mItems, index, mItems, index + 1, mCount - index);
                }

                //assign the new value to the index position of the new buffer
                mItems[index] = value;
                mCount++;
            }
        }

        /// <summary>
        /// Inserts an item to the list at the specified index
        /// </summary>
        void IList.Insert(int index, object value)
        {
            Insert(index, (ListBoxColumn)value);
        }
        #endregion

        #region Is Fixed Size
        /// <summary>
        /// Gets a value indicating whether the list has a fixed size.
        /// </summary>
        public bool IsFixedSize
        { get { return false; } }
        #endregion

        #region Is Read Only
        /// <summary>
        /// Gets a value indicating whether the list is read-only.
        /// </summary>
        public bool IsReadOnly
        { get { return false; } }
        #endregion

        #region Remove
        /// <summary>
        /// Removes the first occurrence of a specific object from the list.
        /// </summary>
        public void Remove(ListBoxColumn value)
        {
            int index = IndexOf(value);
            if (index >= 0) RemoveAt(index);
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the list.
        /// </summary>
        void IList.Remove(object value)
        {
            int index = IndexOf((ListBoxColumn)value);
            if (index >= 0) RemoveAt(index);
        }
        #endregion

        #region Remove At
        /// <summary>
        /// Removes the list item at the specified index
        /// </summary>
        public void RemoveAt(int index)
        {
            mCount--;
            if (index < mCount)
            {
                //copy old values from the right of index
                Array.Copy(mItems, index + 1, mItems, index, mCount - index);
            }
            mItems[mCount] = null;
        }
        #endregion

        #region Indexer
        /// <summary>
        /// Gets or sets the element at the specified index.
        /// </summary>
        public ListBoxColumn this[int index]
        {
            get { return mItems[index]; }
            set { mItems[index] = value; }
        }

        /// <summary>
        /// Gets or sets the element at the specified index.
        /// </summary>
        object IList.this[int index]
        {
            get { return mItems[index]; }
            set { mItems[index] = (ListBoxColumn)value; }
        }
        #endregion

        #endregion

        #region ICollection Members
        #region Copy To
        /// <summary>
        /// Copies the elements of the collection to an array
        /// </summary>
        /// <param name="array">The one-dimensional array that is the destination of the elements copied from collection. The array must have zero-based indexing.</param>
        public void CopyTo(Array array)
        {
            Array.Copy(mItems, 0, array, 0, mCount);
        }

        /// <summary>
        /// Copies the elements of the collection to an array, starting at a particular array index. 
        /// </summary>
        /// <param name="array">The one-dimensional array that is the destination of the elements copied from collection. The array must have zero-based indexing.</param>
        /// <param name="index">The zero-based index in array at which copying begins.</param>
        public void CopyTo(Array array, int index)
        {
            Array.Copy(mItems, 0, array, index, mCount);
        }

        /// <summary>
        /// Copies the elements of the collection to an array, starting at a particular array index. 
        /// </summary>
        /// <param name="array">The one-dimensional array that is the destination of the elements copied from collection. The array must have zero-based indexing.</param>
        /// <param name="index">The zero-based index in this collection at which copying begins.</param>
        /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
        /// <param name="count">The number of elements to copy.</param>
        public void CopyTo(int index, Array array, int arrayIndex, int count)
        {
            Array.Copy(mItems, index, array, arrayIndex, count);
        }
        #endregion

        #region Count
        /// <summary>
        /// Gets the number of elements contained in the ICollection.
        /// </summary>
        public int Count
        { get { return mCount; } }
        #endregion

        #region Is Synchronized
        /// <summary>
        /// Gets a value indicating whether access to the ICollection is synchronized (thread safe).
        /// </summary>
        public bool IsSynchronized
        { get { return false; } }
        #endregion

        #region SyncRoot
        /// <summary>
        /// Gets an object that can be used to synchronize access to the ICollection.
        /// </summary>
        public object SyncRoot
        {
            get
            {
                if (mSynchRoot == null)
                {
                    System.Threading.Interlocked.CompareExchange(ref mSynchRoot, new object(), null);
                }
                return mSynchRoot;
            }
        }
        #endregion

        #endregion

        #region IEnumerable Members

        #region Get Enumerator
        /// <summary>
        /// Returns an enumerator that iterates through this collection
        /// </summary>
        public IEnumerator GetEnumerator()
        {
            return new ListboxColumnListEnumerator(this);
        }

        #region Enumerator
        /// <summary>
        /// Strongly typed enumerator of ListboxColumn.
        /// </summary>
        public class ListboxColumnListEnumerator : object, System.Collections.IEnumerator
        {
            #region privates
            /// <summary>
            /// Current index
            /// </summary>
            private int mIndex;

            /// <summary>
            /// Current element pointed to.
            /// </summary>
            private ListBoxColumn mCurrentElement;

            /// <summary>
            /// Collection to enumerate.
            /// </summary>
            private ListBoxColumnList mCollection;
            #endregion

            #region Constructor
            /// <summary>
            /// Default constructor for enumerator.
            /// </summary>
            /// <param name="collection">Instance of the collection to enumerate.</param>
            internal ListboxColumnListEnumerator(ListBoxColumnList collection)
            {
                mIndex = -1;
                mCollection = collection;
            }
            #endregion

            #region Current
            /// <summary>
            /// Gets the ListboxColumn object in the enumerated ListboxColumnCollection currently indexed by this instance.
            /// </summary>
            public ListBoxColumn Current
            { get { return mCurrentElement; } }

            /// <summary>
            /// Gets the current element in the collection.
            /// </summary>
            object System.Collections.IEnumerator.Current
            { get { return mCurrentElement; } }
            #endregion

            #region Reset
            /// <summary>
            /// Reset the cursor, so it points to the beginning of the enumerator.
            /// </summary>
            public void Reset()
            {
                mIndex = -1;
                mCurrentElement = null;
            }
            #endregion

            #region MoveNext
            /// <summary>
            /// Advances the enumerator to the next queue of the enumeration, if one is currently available.
            /// </summary>
            /// <returns>true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration.</returns>
            public bool MoveNext()
            {
                mIndex++;
                if (mIndex < mCollection.mCount)
                {
                    mCurrentElement = mCollection.mItems[mIndex];
                    return true;
                }
                mIndex = mCollection.mCount;
                return false;
            }
            #endregion
        }
        #endregion

        #endregion

        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor.
        /// <para>Default capacity is 50</para>
        /// </summary>
        public ListBoxColumnList()
        {
            mCapacity = 50;
            mItems = new ListBoxColumn[mCapacity];
        }

        /// <summary>
        /// Create a new instance with the specified capacity
        /// </summary>
        public ListBoxColumnList(int capacity)
        {
            mCapacity = capacity;
            mItems = new ListBoxColumn[capacity];
        }
        #endregion
    }
}
